/*******************************************************************************
 * Descharts library
 * Copyright (c) 2014 Bradipao <bradipao@gmail.com>
 * https://plus.google.com/+SidBradipao
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package it.bradipao.lib.descharts;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.render.Texture;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Size;

import java.util.ArrayList;

/**
 * XyChartView() class represents an XY graph widget.
 *
 * @since 2021-07-27
 */
public class XyChartView extends CartesianView {
    private ArrayList<ChartPointSerie> mSeries = new ArrayList<ChartPointSerie>();
    private Paint mPnt = new Paint();

    /**
     * Constructor.
     *
     * @param context context
     */
    public XyChartView(Context context) {
        super(context);
        init();
    }

    /**
     * Constructor.
     *
     * @param context context
     * @param attrs attrs
     */
    public XyChartView(Context context, AttrSet attrs) {
        super(context, attrs);
        init();
    }
    /**
     * Draws the plot.
     *
     * @param component component
     * @param cnv cnv
     */
    @Override
    public void onDraw(Component component, Canvas cnv) {
        if ((mBmp == null) || isRedraw) {

            getViewSizes();
            getxyminmax();
            if (isXscaleAuto) {
                calcXgridRange();
            }
            if (isYscaleAuto) {
                calcYgridRange();
            }
            calcxycoefs();
            PixelMap.InitializationOptions options = new PixelMap.InitializationOptions();
            options.pixelFormat = PixelFormat.ARGB_8888;
            options.size = new Size(mWidth, mHeight);
            mBmp = PixelMap.create(options);
            Texture texture = new Texture(mBmp);
            mCnv = new Canvas(texture);

            if (isGridVis) {
                drawGrid();
            }
            if (isXtextVis) {
                drawXlabel();
            }
            if (isYtextVis) {
                drawYlabel();
            }
            if (isBorderVis) {
                drawBorder();
            }
            if (isAxisVis) {
                drawAxis();
            }
            drawData();
            isRedraw = false;
        }
        cnv.drawPixelMapHolder(new PixelMapHolder(mBmp), 0, 0, new Paint());
    }

    /**
     * Clears ArrayList of series.
     */
    public void clearSeries() {
        while (mSeries.size() > 0) {
            mSeries.remove(0);
        }
        isRedraw = true;
        invalidate();
    }

    /**
     * Adds a serie to data holder ArrayList.
     *
     * @param serie serie
     */
    public void addSerie(ChartPointSerie serie) {
        mSeries.add(serie);
        isRedraw = true;
        invalidate();
    }

    /**
     * Returns ArrayList of series.
     *
     * @return getSeries
     */
    public ArrayList<ChartPointSerie> getSeries() {
        return mSeries;
    }

    /**
     * Sets line visibility.
     *
     * @param index index
     * @param show show
     */
    public void setLineVis(int index, boolean show) {
        mSeries.get(index).setVisible(show);
        isRedraw = true;
        invalidate();
    }

    /**
     * Sets line color and size.
     *
     * @param index index
     * @param color color
     * @param size size
     */
    public void setLineStyle(int index, int color, float size) {
        mSeries.get(index).setStyle(color, size);
        isRedraw = true;
        invalidate();
    }

    /**
     * Sets line color and size and dip.
     *
     * @param index index
     * @param color color
     * @param size size
     * @param usedip usedip
     */
    public void setLineStyle(int index, int color, float size, boolean usedip) {
        mSeries.get(index).setStyle(color, size, usedip);
        isRedraw = true;
        invalidate();
    }

    /**
     * Gets X,Y ranges across all series
     */
    protected void getxyminmax() {
        ChartPointSerie serie;
        for (mI = 0; mI < mSeries.size(); mI++) {
            serie = mSeries.get(mI);
            if (mI == 0) {
                mXmin = serie.mXmin;
                mXmax = serie.mXmax;
                mYmin = serie.mYmin;
                mYmax = serie.mYmax;
            } else {
                if (serie.mXmin < mXmin) mXmin = serie.mXmin;
                if (serie.mXmax > mXmax) mXmax = serie.mXmax;
                if (serie.mYmin < mYmin) mYmin = serie.mYmin;
                if (serie.mYmax > mYmax) mYmax = serie.mYmax;
            }
        }
    }

    /**
     * Draw data from all series
     */
    protected void drawData() {
        float pX, pY;
        boolean pValid;
        ChartPoint point;
        for (ChartPointSerie serie : mSeries) {
            if (serie.isVisible()) {
                // set paint
                mPnt.reset();
                mPnt.setStyle(Paint.Style.STROKE_STYLE);
                mPnt.setColor(new Color(serie.mColor));
                if (serie.mUseDip) mPnt.setStrokeWidth(dipToPixel(serie.mWidth));
                else mPnt.setStrokeWidth(serie.mWidth);
                mPnt.setAntiAlias(true);
                // iterate through points
                pValid = false;
                mPath.reset();
                for (mI = 0; mI < serie.mPointList.size(); mI++) {
                    point = serie.mPointList.get(mI);
                    pX = point.mX;
                    pY = point.mY;
                    if (Float.isNaN(pX) || Float.isNaN(pY)) {
                        pValid = false;
                    } else if (!pValid) {
                        mPath.moveTo(mX + (pX - mBx) * mAx, mEy - (pY - mBy) * mAy);
                        pValid = true;
                    } else {
                        mPath.lineTo(mX + (pX - mBx) * mAx, mEy - (pY - mBy) * mAy);
                    }
                }
                // draw line
                mCnv.drawPath(mPath, mPnt);
            }
        }
    }

}
